// Copyright (C) Calum Grant 2008

#include "cppscript"
#include "dynamic/extensions.hpp"
#include "tls.hpp"

using internal::nonroot_var;

static_call( register_pickle_type( cmp_varargs, varargs(null) ) );


namespace dynamic
{
	namespace types
	{

	/// Implementation of functions.
	/** \ingroup  impl */
	template<typename Derived, var_cmp_index CmpIndex>
	class generic_function_impl : public var_impl
	{
	public:

		void output(ostream & os) 
		{ 
			os << "<<function>>"; 
		}

		void output(wostream & ss) 
		{ 
			ss << L"<<function>>";
		}

		std::string class_name() { return "function"; }
	
		var_cmp_result compare2(const var & other)
		{
			void * p1 = as_ptr(), *p2 = other.impl().as_ptr();
			if(p1<p2) return cmp_lt;
			if(p1>p2) return cmp_gt;
			return cmp_equal;
		}

		void copy_to(void * dest) const
		{
			new(dest) Derived( *static_cast<const Derived*>(this) );
		}

		var_cmp_index comparison_index() { return CmpIndex; }

		void pickle(pickler & p)
		{
			p.write_object_type(CmpIndex);
			p.write_string( internal::lookup_function( as_ptr() ) );
		}
	};


	/// Implements a function.
	/** @param Fn is the underlying type of the function */
	/** \ingroup  impl */
	template<typename Fn>
	class function_impl;

	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<f0> : 
		public generic_function_impl<function_impl<f0>, cmp_function_0 >
	{
		f0 m_fn;
	public:
		function_impl(f0 f) : m_fn(f) { }
		var call() { return m_fn(); }
		int max_args() { return 0; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<vf0> : 
		public generic_function_impl<function_impl<vf0>, cmp_function_v0 >
	{
		vf0 m_fn;
	public:
		function_impl(vf0 f) : m_fn(f) { }
		var call() { m_fn(); return var(); }
		int max_args() { return 0; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<f1> : 
		public generic_function_impl<function_impl<f1>, cmp_function_1 >
	{
		f1 m_fn;
	public:
		function_impl(f1 f) : m_fn(f) { }
		var call(const var & a0) { return m_fn(a0); }
		int max_args() { return 1; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<vf1> : 
		public generic_function_impl<function_impl<vf1>, cmp_function_v1 >
	{
		vf1 m_fn;
	public:
		function_impl(vf1 f) : m_fn(f) { }
		var call(const var & a0) { m_fn(a0); return var(); }
		int max_args() { return 1; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<f2> : 
		public generic_function_impl<function_impl<f2>, cmp_function_2 >
	{
		f2 m_fn;
	public:
		function_impl(f2 f) : m_fn(f) { }
		var call(const var & a0, const var & a1) { return m_fn(a0,a1); }
		int max_args() { return 2; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<vf2> : 
		public generic_function_impl<function_impl<vf2>, cmp_function_v2 >
	{
		vf2 m_fn;
	public:
		function_impl(vf2 f) : m_fn(f) { }
		var call(const var & a0, const var & a1) { m_fn(a0,a1); return var(); }
		int max_args() { return 2; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<f3> : 
		public generic_function_impl<function_impl<f3>, cmp_function_3 >
	{
		f3 m_fn;
	public:
		function_impl(f3 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2) { return m_fn(a0,a1,a2); }
		int max_args() { return 3; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<vf3> : 
		public generic_function_impl<function_impl<vf3>, cmp_function_v3  >
	{
		vf3 m_fn;
	public:
		function_impl(vf3 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2) 
		{ 
			m_fn(a0,a1,a2); return var(); 
		}
		int max_args() { return 3; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<f4> : 
		public generic_function_impl<function_impl<f4>, cmp_function_4 >
	{
		f4 m_fn;
	public:
		function_impl(f4 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3) 
		{ 
			return m_fn(a0,a1,a2,a3); 
		}
		int max_args() { return 4; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<vf4> : 
		public generic_function_impl<function_impl<vf4>, cmp_function_v4 >
	{
		vf4 m_fn;
	public:
		function_impl(vf4 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3) 
		{ m_fn(a0,a1,a2,a3); return var(); }
		int max_args() { return 4; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<f5> : 
		public generic_function_impl<function_impl<f5>, cmp_function_5 >
	{
		f5 m_fn;
	public:
		function_impl(f5 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4) 
		{ return m_fn(a0,a1,a2,a3,a4); }
		int max_args() { return 5; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<vf5> : 
		public generic_function_impl<function_impl<vf5>, cmp_function_v5 >
	{
		vf5 m_fn;
	public:
		function_impl(vf5 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4) 
		{ m_fn(a0,a1,a2,a3,a4); return var(); }
		int max_args() { return 5; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<f6> : 
		public generic_function_impl<function_impl<f6>, cmp_function_6>
	{
		f6 m_fn;
	public:
		function_impl(f6 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5) 
		{ return m_fn(a0,a1,a2,a3,a4,a5); }
		int max_args() { return 6; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<vf6> : 
		public generic_function_impl<function_impl<vf6>, cmp_function_v6>
	{
		vf6 m_fn;
	public:
		function_impl(vf6 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5) 
		{ m_fn(a0,a1,a2,a3,a4,a5); return var(); }
		int max_args() { return 6; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<f7> : 
		public generic_function_impl<function_impl<f7>, cmp_function_7>
	{
		f7 m_fn;
	public:
		function_impl(f7 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6) 
		{ return m_fn(a0,a1,a2,a3,a4,a5,a6); }
		int max_args() { return 7; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<vf7> : 
		public generic_function_impl<function_impl<vf7>, cmp_function_v7>
	{
		vf7 m_fn;
	public:
		function_impl(vf7 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6) 
		{ m_fn(a0,a1,a2,a3,a4,a5,a6); return var(); }
		int max_args() { return 7; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<f8> : 
		public generic_function_impl<function_impl<f8>, cmp_function_8>
	{
		f8 m_fn;
	public:
		function_impl(f8 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7) 
		{ return m_fn(a0,a1,a2,a3,a4,a5,a6,a7); }
		int max_args() { return 8; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<vf8> : 
		public generic_function_impl<function_impl<vf8>, cmp_function_v8>
	{
		vf8 m_fn;
	public:
		function_impl(vf8 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7) 
		{ m_fn(a0,a1,a2,a3,a4,a5,a6,a7); return var(); }
		int max_args() { return 8; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<f9> : 
		public generic_function_impl<function_impl<f9>, cmp_function_9>
	{
		f9 m_fn;
	public:
		function_impl(f9 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8) 
		{ return m_fn(a0,a1,a2,a3,a4,a5,a6,a7,a8); }
		int max_args() { return 9; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<vf9> : 
		public generic_function_impl<function_impl<vf9>, cmp_function_v9>
	{
		vf9 m_fn;
	public:
		function_impl(vf9 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8) 
		{ m_fn(a0,a1,a2,a3,a4,a5,a6,a7,a8); return var(); }
		int max_args() { return 9; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<f10> : 
		public generic_function_impl<function_impl<f10>, cmp_function_10>
	{
		f10 m_fn;
	public:
		function_impl(f10 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8, const var & a9) 
		{ return m_fn(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9); }
		int max_args() { return 10; }
		void * as_ptr() { return (void*)m_fn; }
	};


	/// Implements a function.
	/** \ingroup  impl */
	template<> class function_impl<vf10> : 
		public generic_function_impl<function_impl<vf10>, cmp_function_v10>
	{
		vf10 m_fn;
	public:
		function_impl(vf10 f) : m_fn(f) { }
		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8, const var & a9) 
		{ m_fn(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9); return var(); }
		int max_args() { return 10; }
		void * as_ptr() { return (void*)m_fn; }
	};
	}
}


template<typename Fn>
var dynamic::internal::function(Fn f)
{
	return var(types::function_impl<Fn>(f), var::assign_impl());
}


template var dynamic::internal::function<f0>(f0);
template var dynamic::internal::function<f1>(f1);
template var dynamic::internal::function<f2>(f2);
template var dynamic::internal::function<f3>(f3);
template var dynamic::internal::function<f4>(f4);
template var dynamic::internal::function<f5>(f5);
template var dynamic::internal::function<f6>(f6);
template var dynamic::internal::function<f7>(f7);
template var dynamic::internal::function<f8>(f8);
template var dynamic::internal::function<f9>(f9);
template var dynamic::internal::function<f10>(f10);

template var dynamic::internal::function<vf0>(vf0);
template var dynamic::internal::function<vf1>(vf1);
template var dynamic::internal::function<vf2>(vf2);
template var dynamic::internal::function<vf3>(vf3);
template var dynamic::internal::function<vf4>(vf4);
template var dynamic::internal::function<vf5>(vf5);
template var dynamic::internal::function<vf6>(vf6);
template var dynamic::internal::function<vf7>(vf7);
template var dynamic::internal::function<vf8>(vf8);
template var dynamic::internal::function<vf9>(vf9);
template var dynamic::internal::function<vf10>(vf10);

namespace
{
	// bind 1
	var cc(const var & functor, nonroot_var (&args)[1])
	{ return functor(args[0].get()); }

	var cc(const var & functor, nonroot_var (&args)[1], const var & a0)
	{ return functor(args[0].get(), a0); }

	var cc(const var & functor, nonroot_var (&args)[1], const var & a0, const var & a1)
	{ return functor(args[0].get(), a0, a1); }

	var cc(const var & functor, nonroot_var (&args)[1], const var & a0, const var & a1, const var & a2)
	{ return functor(args[0].get(), a0, a1, a2); }

	var cc(const var & functor, nonroot_var (&args)[1], const var & a0, const var & a1, const var & a2, const var & a3)
	{ return functor(args[0].get(), a0, a1, a2, a3); }

	var cc(const var & functor, nonroot_var (&args)[1], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4)
	{ return functor(args[0].get(), a0, a1, a2, a3, a4); }

	var cc(const var & functor, nonroot_var (&args)[1], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5)
	{ return functor(args[0].get(), a0, a1, a2, a3, a4, a5); }

	var cc(const var & functor, nonroot_var (&args)[1], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6)
	{ return functor(args[0].get(), a0, a1, a2, a3, a4, a5, a6); }

	var cc(const var & functor, nonroot_var (&args)[1], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7)
	{ return functor(args[0].get(), a0, a1, a2, a3, a4, a5, a6, a7); }

	var cc(const var & functor, nonroot_var (&args)[1], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8)
	{ return functor(args[0].get(), a0, a1, a2, a3, a4, a5, a6, a7, a8); }

	// bind 2
	var cc(const var & functor, nonroot_var (&args)[2])
	{ return functor(args[0].get(), args[1].get()); }

	var cc(const var & functor, nonroot_var (&args)[2], const var & a0)
	{ return functor(args[0].get(), args[1].get(), a0); }

	var cc(const var & functor, nonroot_var (&args)[2], const var & a0, const var & a1)
	{ return functor(args[0].get(), args[1].get(), a0, a1); }

	var cc(const var & functor, nonroot_var (&args)[2], const var & a0, const var & a1, const var & a2)
	{ return functor(args[0].get(), args[1].get(), a0, a1, a2); }

	var cc(const var & functor, nonroot_var (&args)[2], const var & a0, const var & a1, const var & a2, const var & a3)
	{ return functor(args[0].get(), args[1].get(), a0, a1, a2, a3); }

	var cc(const var & functor, nonroot_var (&args)[2], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4)
	{ return functor(args[0].get(), args[1].get(), a0, a1, a2, a3, a4); }

	var cc(const var & functor, nonroot_var (&args)[2], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5)
	{ return functor(args[0].get(), args[1].get(), a0, a1, a2, a3, a4, a5); }

	var cc(const var & functor, nonroot_var (&args)[2], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6)
	{ return functor(args[0].get(), args[1].get(), a0, a1, a2, a3, a4, a5, a6); }

	var cc(const var & functor, nonroot_var (&args)[2], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7)
	{ return functor(args[0].get(), args[1].get(), a0, a1, a2, a3, a4, a5, a6, a7); }

	template<std::size_t N>
	var cc(const var & functor, nonroot_var (&args)[N], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8)
	{ throw too_many_arguments(); }

	// bind 3
	var cc(const var & functor, nonroot_var (&args)[3])
	{ return functor(args[0].get(), args[1].get(), args[2].get()); }

	var cc(const var & functor, nonroot_var (&args)[3], const var & a0)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), a0); }

	var cc(const var & functor, nonroot_var (&args)[3], const var & a0, const var & a1)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), a0, a1); }

	var cc(const var & functor, nonroot_var (&args)[3], const var & a0, const var & a1, const var & a2)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), a0, a1, a2); }

	var cc(const var & functor, nonroot_var (&args)[3], const var & a0, const var & a1, const var & a2, const var & a3)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), a0, a1, a2, a3); }

	var cc(const var & functor, nonroot_var (&args)[3], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), a0, a1, a2, a3, a4); }

	var cc(const var & functor, nonroot_var (&args)[3], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), a0, a1, a2, a3, a4, a5); }

	var cc(const var & functor, nonroot_var (&args)[3], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), a0, a1, a2, a3, a4, a5, a6); }

	template<std::size_t N>
	var cc(const var & functor, nonroot_var (&args)[N], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7)
	{ throw too_many_arguments(); }

	// bind 4

	var cc(const var & functor, nonroot_var (&args)[4])
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get()); }

	var cc(const var & functor, nonroot_var (&args)[4], const var & a0)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), a0); }

	var cc(const var & functor, nonroot_var (&args)[4], const var & a0, const var & a1)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), a0, a1); }

	var cc(const var & functor, nonroot_var (&args)[4], const var & a0, const var & a1, const var & a2)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), a0, a1, a2); }

	var cc(const var & functor, nonroot_var (&args)[4], const var & a0, const var & a1, const var & a2, const var & a3)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), a0, a1, a2, a3); }

	var cc(const var & functor, nonroot_var (&args)[4], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), a0, a1, a2, a3, a4); }

	var cc(const var & functor, nonroot_var (&args)[4], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), a0, a1, a2, a3, a4, a5); }

	template<std::size_t N>
	var cc(const var & functor, nonroot_var (&args)[N], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6)
	{ throw too_many_arguments(); }

	// bind 5

	var cc(const var & functor, nonroot_var (&args)[5])
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get()); }

	var cc(const var & functor, nonroot_var (&args)[5], const var & a0)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), a0); }

	var cc(const var & functor, nonroot_var (&args)[5], const var & a0, const var & a1)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), a0, a1); }

	var cc(const var & functor, nonroot_var (&args)[5], const var & a0, const var & a1, const var & a2)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), a0, a1, a2); }

	var cc(const var & functor, nonroot_var (&args)[5], const var & a0, const var & a1, const var & a2, const var & a3)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), a0, a1, a2, a3); }

	var cc(const var & functor, nonroot_var (&args)[5], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), a0, a1, a2, a3, a4); }

	template<std::size_t N>
	var cc(const var & functor, nonroot_var (&args)[N], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5)
	{ throw too_many_arguments(); }

	// bind 6

	var cc(const var & functor, nonroot_var (&args)[6])
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get()); }

	var cc(const var & functor, nonroot_var (&args)[6], const var & a0)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), a0); }

	var cc(const var & functor, nonroot_var (&args)[6], const var & a0, const var & a1)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), a0, a1); }

	var cc(const var & functor, nonroot_var (&args)[6], const var & a0, const var & a1, const var & a2)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), a0, a1, a2); }

	var cc(const var & functor, nonroot_var (&args)[6], const var & a0, const var & a1, const var & a2, const var & a3)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), a0, a1, a2, a3); }

	template<std::size_t N>
	var cc(const var & functor, nonroot_var (&args)[N], const var & a0, const var & a1, const var & a2, const var & a3, const var & a4)
	{ throw too_many_arguments(); }

	// bind 7

	var cc(const var & functor, nonroot_var (&args)[7])
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), args[6].get()); }

	var cc(const var & functor, nonroot_var (&args)[7], const var & a0)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), args[6].get(), a0); }

	var cc(const var & functor, nonroot_var (&args)[7], const var & a0, const var & a1)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), args[6].get(), a0, a1); }

	var cc(const var & functor, nonroot_var (&args)[7], const var & a0, const var & a1, const var & a2)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), args[6].get(), a0, a1, a2); }

	template<std::size_t N>
	var cc(const var & functor, nonroot_var (&args)[N], const var & a0, const var & a1, const var & a2, const var & a3)
	{ throw too_many_arguments(); }

	// bind 8

	var cc(const var & functor, nonroot_var (&args)[8])
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), args[6].get(), args[7].get()); }

	var cc(const var & functor, nonroot_var (&args)[8], const var & a0)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), args[6].get(), args[7].get(), a0); }

	var cc(const var & functor, nonroot_var (&args)[8], const var & a0, const var & a1)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), args[6].get(), args[7].get(), a0, a1); }

	template<std::size_t N>
	var cc(const var & functor, nonroot_var (&args)[N], const var & a0, const var & a1, const var & a2)
	{ throw too_many_arguments(); }

	// bind 9

	var cc(const var & functor, nonroot_var (&args)[9])
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), args[6].get(), args[7].get(), args[8].get()); }

	var cc(const var & functor, nonroot_var (&args)[9], const var & a0)
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), args[6].get(), args[7].get(), args[8].get(), a0); }

	template<std::size_t N>
	var cc(const var & functor, nonroot_var (&args)[N], const var & a0, const var & a1)
	{ throw too_many_arguments(); }

	// bind 10

	var cc(const var & functor, nonroot_var (&args)[10])
	{ return functor(args[0].get(), args[1].get(), args[2].get(), args[3].get(), args[4].get(), args[5].get(), args[6].get(), args[7].get(), args[8].get(), args[9].get()); }

	template<std::size_t N>
	var cc(const var & functor, nonroot_var (&args)[N], const var & a0)
	{ throw too_many_arguments(); }

}

namespace dynamic
{
	namespace types
	{
		/// Implements bind/method to bind a number of arguments to a functor.
		/** \ingroup  impl */
		template<int Args>
		class bind_impl : public shared_var_impl
		{
			nonroot_var m_functor;
			nonroot_var m_arg[Args];

		public:

			int max_args() { return m_functor.ref().max_args() - Args; }
			int bind_args() { return Args; }

			var_cmp_index comparison_index() { return cmp_bind; }

			var_cmp_result compare2(const var & other)
			{
				int cmp = Args - other.impl().bind_args();
				if(cmp<0) return cmp_lt;
				if(cmp>0) return cmp_gt;

				bind_impl * other_bind = dynamic_cast<bind_impl*>(other.impl().shared_var());
				
				if(!other_bind) return cmp_not_equal;	// Should not happen

				var_cmp_result result = m_functor.ref().impl().compare(other_bind->m_functor.ref());
				if(result != cmp_equal) return result;

				for(int i=0; i<Args; ++i)
				{
					result = m_arg[i].ref().impl().compare(other_bind->m_arg[i].ref());
					if(result != cmp_equal) return result;
				}
				return cmp_equal;
			}

			void pickle(pickler & p)
			{
				p.write_object_type(cmp_bind);
				p.write_int(Args);
				p.write_object(m_functor.ref());
				for(int i=0; i<Args; ++i)
					p.write_object(m_arg[i].ref());
			}

			void unpickle(unpickler & p)
			{
				m_functor = p.read_object();
				for(int i=0; i<Args; ++i)
					m_arg[i] = p.read_object();
			}

			bind_impl()
			{
			}

			bind_impl(const nonroot_var & functor, const nonroot_var & new_obj, const nonroot_var * args) :
				m_functor(functor)
			{ 
				m_arg[0] = new_obj; 
				for(int i=1; i<Args; ++i)
					m_arg[i] = args[i];
			}

			bind_impl(const var & functor, const var & a0) :
				m_functor(functor)
			{ m_arg[0] = a0; }

			bind_impl(const var & functor, const var & a0, const var & a1) :
				m_functor(functor)
			{ 
				m_arg[0] = a0; 
				m_arg[1] = a1;
			}

			bind_impl(const var & functor, const var & a0, const var & a1, const var & a2) :
				m_functor(functor)
			{ 
				m_arg[0] = a0; 
				m_arg[1] = a1;
				m_arg[2] = a2;
			}

			bind_impl(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3) :
				m_functor(functor)
			{ 
				m_arg[0] = a0; 
				m_arg[1] = a1;
				m_arg[2] = a2;
				m_arg[3] = a3;
			}

			bind_impl(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4) :
				m_functor(functor)
			{ 
				m_arg[0] = a0; 
				m_arg[1] = a1;
				m_arg[2] = a2;
				m_arg[3] = a3;
				m_arg[4] = a4;
			}

			bind_impl(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5) :
				m_functor(functor)
			{ 
				m_arg[0] = a0; 
				m_arg[1] = a1;
				m_arg[2] = a2;
				m_arg[3] = a3;
				m_arg[4] = a4;
				m_arg[5] = a5;
			}

			bind_impl(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6) :
				m_functor(functor)
			{ 
				m_arg[0] = a0; 
				m_arg[1] = a1;
				m_arg[2] = a2;
				m_arg[3] = a3;
				m_arg[4] = a4;
				m_arg[5] = a5;
				m_arg[6] = a6;
			}

			bind_impl(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7) :
				m_functor(functor)
			{ 
				m_arg[0] = a0; 
				m_arg[1] = a1;
				m_arg[2] = a2;
				m_arg[3] = a3;
				m_arg[4] = a4;
				m_arg[5] = a5;
				m_arg[6] = a6;
				m_arg[7] = a7;
			}

			bind_impl(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8) :
				m_functor(functor)
			{ 
				m_arg[0] = a0; 
				m_arg[1] = a1;
				m_arg[2] = a2;
				m_arg[3] = a3;
				m_arg[4] = a4;
				m_arg[5] = a5;
				m_arg[6] = a6;
				m_arg[7] = a7;
				m_arg[8] = a8;
			}

			bind_impl(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8, const var & a9) :
				m_functor(functor)
			{ 
				m_arg[0] = a0; 
				m_arg[1] = a1;
				m_arg[2] = a2;
				m_arg[3] = a3;
				m_arg[4] = a4;
				m_arg[5] = a5;
				m_arg[6] = a6;
				m_arg[7] = a7;
				m_arg[8] = a8;
				m_arg[9] = a9;
			}

			std::string class_name() { return "bind"; }

			void output(ostream & ss)
			{
				m_arg[0].ref().impl().output(ss);
				ss << '.';
				foreach(member, m_arg[0].ref())
				{
					if(m_arg[0].ref()[member.as_string().c_str()] == var(this))
					{
						member.impl().output(ss);
						return;
					}
				}
				ss << "<<bind>>";
			}

			void output(wostream & ss)
			{
				m_arg[0].ref().impl().output(ss);
				ss << L'.';
				foreach(member, m_arg[0].ref())
				{
					if(m_arg[0].ref()[member] == var(this))
					{
						member.impl().output(ss);
						return;
					}
				}
				ss << L"<<bind>>";
			}

			void mark_children(gc::garbage_collector & gc)
			{
				m_functor.ref().impl().mark_reachable(gc);
				for(int i=0; i<Args; ++i)
					m_arg[i].ref().impl().mark_reachable(gc);
			}

			var call() { stack_trace_entry e(this); return cc(m_functor.ref(), m_arg); }

			var call(const var & a0)
			{ stack_trace_entry e(this); return cc(m_functor.get(), m_arg, a0); }

			var call(const var & a0, const var & a1)
			{ stack_trace_entry e(this); return cc( m_functor.get(), m_arg, a0, a1 ); }

			var call(const var & a0, const var & a1, const var & a2)
			{ stack_trace_entry e(this); return cc( m_functor.get(), m_arg, a0, a1, a2 ); }

			var call(const var & a0, const var & a1, const var & a2, const var & a3)
			{ stack_trace_entry e(this); return cc( m_functor.get(), m_arg, a0, a1, a2, a3 ); }

			var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4)
			{ stack_trace_entry e(this); return cc( m_functor.get(), m_arg, a0, a1, a2, a3, a4 ); }

			var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5)
			{ stack_trace_entry e(this); return cc( m_functor.get(), m_arg, a0, a1, a2, a3, a4, a5 ); }

			var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6)
			{ stack_trace_entry e(this); return cc( m_functor.get(), m_arg, a0, a1, a2, a3, a4, a5, a6 ); }

			var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7)
			{ stack_trace_entry e(this); return cc( m_functor.get(), m_arg, a0, a1, a2, a3, a4, a5, a6, a7 ); }

			var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8)
			{ stack_trace_entry e(this); return cc( m_functor.get(), m_arg, a0, a1, a2, a3, a4, a5, a6, a7, a8 ); }
		};
	}
}


var dynamic::bind(const var & functor, const var & arg)
{
	if(functor.max_args() < 1) throw too_many_arguments();
	return new types::bind_impl<1>(functor, arg);
}


var dynamic::bind(const var & functor, const var & a0, const var & a1)
{
	if(functor.max_args() < 2) throw too_many_arguments();
	return new types::bind_impl<2>(functor, a0, a1);
}


var dynamic::bind(const var & functor, const var & a0, const var & a1, const var & a2)
{
	if(functor.max_args() < 3) throw too_many_arguments();
	return new types::bind_impl<3>(functor, a0, a1, a2);
}


var dynamic::bind(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3)
{
	if(functor.max_args() < 4) throw too_many_arguments();
	return new types::bind_impl<4>(functor, a0, a1, a2, a3);
}


var dynamic::bind(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4)
{
	if(functor.max_args() < 5) throw too_many_arguments();
	return new types::bind_impl<5>(functor, a0, a1, a2, a3, a4);
}


var dynamic::bind(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5)
{
	if(functor.max_args() < 6) throw too_many_arguments();
	return new types::bind_impl<6>(functor, a0, a1, a2, a3, a4, a5);
}


var dynamic::bind(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6)
{
	if(functor.max_args() < 7) throw too_many_arguments();
	return new types::bind_impl<7>(functor, a0, a1, a2, a3, a4, a5, a6);
}


var dynamic::bind(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7)
{
	if(functor.max_args() < 8) throw too_many_arguments();
	return new types::bind_impl<8>(functor, a0, a1, a2, a3, a4, a5, a6, a7);
}


var dynamic::bind(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8)
{
	if(functor.max_args() < 9) throw too_many_arguments();
	return new types::bind_impl<9>(functor, a0, a1, a2, a3, a4, a5, a6, a7, a8);
}


var dynamic::bind(const var & functor, const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8, const var & a9)
{
	if(functor.max_args() < 10) throw too_many_arguments();
	return new types::bind_impl<10>(functor, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
}


var dynamic::internal::bind(int args)
{
	switch(args)
	{
	case 1: return new types::bind_impl<1>();
	case 2: return new types::bind_impl<2>();
	case 3: return new types::bind_impl<3>();
	case 4: return new types::bind_impl<4>();
	case 5: return new types::bind_impl<5>();
	case 6: return new types::bind_impl<6>();
	case 7: return new types::bind_impl<7>();
	case 8: return new types::bind_impl<8>();
	case 9: return new types::bind_impl<9>();
	case 10: return new types::bind_impl<10>();
	default:
		throw invalid_string();
	}
}


namespace dynamic
{
namespace types
{
	/// Implements the varargs() object.
	/** \ingroup  impl */
	class varargs_impl : public shared_var_impl
	{
		nonroot_var m_fn;

	public:
		varargs_impl(const var & fn) : m_fn(fn) 
		{ 
		}

		void mark_children(gc::garbage_collector&gc)
		{
			m_fn.ref().impl().mark_reachable(gc);
		}

		var call()
		{
			return m_fn.ref()(array());
		}

		var call(const var & a0)
		{
			var a = array();
			a.push_back(a0);
			return m_fn.ref()(a);
		}

		var call(const var & a0, const var & a1)
		{
			var a = array();
			a.push_back(a0);
			a.push_back(a1);
			return m_fn.ref()(a);
		}

		var call(const var & a0, const var & a1, const var & a2)
		{
			var a = array();
			a.push_back(a0);
			a.push_back(a1);
			a.push_back(a2);
			return m_fn.ref()(a);
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3)
		{
			var a = array();
			a.push_back(a0);
			a.push_back(a1);
			a.push_back(a2);
			a.push_back(a3);
			return m_fn.ref()(a);
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4)
		{
			var a = array();
			a.push_back(a0);
			a.push_back(a1);
			a.push_back(a2);
			a.push_back(a3);
			a.push_back(a4);
			return m_fn.ref()(a);
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5)
		{
			var a = array();
			a.push_back(a0);
			a.push_back(a1);
			a.push_back(a2);
			a.push_back(a3);
			a.push_back(a4);
			a.push_back(a5);
			return m_fn.ref()(a);
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6)
		{
			var a = array();
			a.push_back(a0);
			a.push_back(a1);
			a.push_back(a2);
			a.push_back(a3);
			a.push_back(a4);
			a.push_back(a5);
			a.push_back(a6);
			return m_fn.ref()(a);
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7)
		{
			var a = array();
			a.push_back(a0);
			a.push_back(a1);
			a.push_back(a2);
			a.push_back(a3);
			a.push_back(a4);
			a.push_back(a5);
			a.push_back(a6);
			a.push_back(a7);
			return m_fn.ref()(a);
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8)
		{
			var a = array();
			a.push_back(a0);
			a.push_back(a1);
			a.push_back(a2);
			a.push_back(a3);
			a.push_back(a4);
			a.push_back(a5);
			a.push_back(a6);
			a.push_back(a7);
			a.push_back(a8);
			return m_fn.ref()(a);
		}

		var call(const var & a0, const var & a1, const var & a2, const var & a3, const var & a4, const var & a5, const var & a6, const var & a7, const var & a8, const var & a9)
		{
			var a = array();
			a.push_back(a0);
			a.push_back(a1);
			a.push_back(a2);
			a.push_back(a3);
			a.push_back(a4);
			a.push_back(a5);
			a.push_back(a6);
			a.push_back(a7);
			a.push_back(a8);
			a.push_back(a9);
			return m_fn.ref()(a);
		}

		string class_name() 
		{ 
			return "varargs"; 
		}

		var_cmp_index comparison_index() 
		{ 
			return cmp_varargs; 
		}

		var_cmp_result compare2(const var & other_var)
		{
			varargs_impl * other = dynamic_cast<varargs_impl*>(other_var.impl().shared_var() );
			if( !other ) return cmp_not_equal;

			return m_fn.ref().impl().compare(other->m_fn.ref());
		}

		void output(ostream & os)
		{
			os << "varargs(";
			m_fn.ref().impl().output(os);
			os << ")";
		}

		void output(wostream & os)
		{
			os << L"varargs(";
			m_fn.ref().impl().output(os);
			os << L")";
		}

		void pickle(pickler & p)
		{
			p.write_object_type(cmp_varargs);
			m_fn.ref().impl().pickle(p);
		}

		void unpickle(unpickler & p)
		{
			m_fn = p.read_object();
		}

		int max_args() 
		{ 
			return 10; 
		}

		var clone() 
		{ 
			return new varargs_impl(*this); 
		}
	};
}
}


var dynamic::varargs(const var & fn)
{
	return new types::varargs_impl(fn);
}


namespace
{
	typedef cg::tls<stack_trace_entry*> stack_top;
}


dynamic::stack_trace_entry::stack_trace_entry(const var & fn) : m_fn(fn)
{
	m_previous = stack_top::get();
	stack_top::set(this);
}


dynamic::stack_trace_entry::~stack_trace_entry()
{
	stack_top::set(m_previous);
}


var dynamic::stack_trace_entry::stack_trace()
{
	var a = array();
	for(stack_trace_entry * e = stack_top::get(); e; e=e->m_previous)
	{
		a.push_back( e->m_fn );
	}
	return a;
}


var dynamic::stack_trace()
{
	return stack_trace_entry::stack_trace();
}


namespace
{
	// This isn't implemented yet.  Do not use.

	class named_functor_impl : public shared_var_impl
	{
		nonroot_var m_fn;
		std::string m_name;

	public:
		named_functor_impl(const var & f, const char * n) : m_fn(f), m_name(n)
		{
		}

		void mark_children(gc::garbage_collector & gc)
		{
			m_fn.ref().impl().mark_reachable(gc);
		}

		void output(ostream & os)
		{
			os << m_name;
		}

		void output(wostream & os)
		{
			/// \todo Rubbish implementation
			os << std::string(m_name.begin(), m_name.end());
		}

		var call() 
		{
			stack_trace_entry e(this);
			return m_fn.ref().impl().call(); 
		}

		var call(const var & a0) 
		{
			stack_trace_entry e(this);
			return m_fn.ref().impl().call(a0); 
		}

		var call(const var & a0, const var & a1) 
		{
			stack_trace_entry e(this);
			return m_fn.ref().impl().call(a0,a1); 
		}

		var call(const var & a0, const var & a1, const var & a2) 
		{
			stack_trace_entry e(this);
			return m_fn.ref().impl().call(a0,a1,a2); 
		}

		string class_name() 
		{ 
			return "named_functor"; 
		}
	};
}


#define func(X) ::dynamic::internal::named_functor(X, #X)

var dynamic::internal::named_functor(const var &fn, const char * name)
{
	return new named_functor_impl(fn, name);
}
